/** earth.js
*
* The javascript methods to implement the Google Earth viewer.
*
*   Copyright 2011 Bravo369.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* author = Mike Meade (mmeade@google.com)
**/

var headingDeltaPerSecond = 15;
var hold = false;
var index = 0;
var lastHeading = null;;
var lastUpdate = 0;
var log = '';
var magneticVariation = -19;
var positionData = null;
var startTime = 0;
var stuckCheckerTimeout = 2000;
var stuckCount = 0;
var stuckThreshold = 10;
var stuckUpdate = 0;
var timeDiff = 0;


var headingLog = '';
var positionLog = '';

var Heading = function(heading, sample_timestamp) {
  if (heading > 180) heading -= 180;
  else if (heading < -180) heading += 180;
  return {
    value: Number(heading),
    timestamp: sample_timestamp,
    difference: function(other) {
      var thisHeading = this.value + 180;
      var otherHeading = other + 180;
      return otherHeading - thisHeading;
    },
    rateLimitChange: function(actualDifference, timeDelta) {
      var maxDifference = headingDeltaPerSecond * timeDelta;
      if (Math.abs(actualDifference) > maxDifference && actualDifference < 0)
        actualDifference = -1 * maxDifference;
      else if (Math.abs(actualDifference) > maxDifference && actualDifference > 0)
        actualDifference = maxDifference;
      return actualDifference;
    },
    toMagneticHeading: function() {
      var value = this.value + magneticVariation;
      if (value < -180) value += 180;
      else if (value > 180) value -= 180;
      return value;
    }
  };
}

Number.prototype.toRad = function() {
  return this * Math.PI / 180;
}

Number.prototype.toDeg = function() {
  return this * 180.0 / Math.PI;
}

function initGoogle() {
  google.earth.createInstance('map3d', initCB, failureCB);
}

function initCB(instance) {
  ge = instance;
  ge.getWindow().setVisibility(true);
  ge.getOptions().setFlyToSpeed(ge.SPEED_TELEPORT);
  setOverlay();
  initializeAndStart();
  setupStuckChecker();
}

function setupStuckChecker() {
  window.setTimeout('checkStuck();', stuckCheckerTimeout);
}

function initializeAndStart() {
  hold = false;
  index = 0;
  lastHeading = null;
  positionData = null;
  startTime = new Date().getTime();
  stuckCheckerTimeout = 2000;
  stuckCount = 0;
  stuckThreshold = 3;
  stuckUpdate = lastUpdate;
  timeDiff = 0;
  google.earth.removeEventListener(ge.getView(), 'viewchangeend', getNextView);
  google.earth.addEventListener(ge.getView(), 'viewchangeend', getNextView);
  getViewValues();
}

function checkStuck() {
  setupStuckChecker();
  if (!lastUpdate) {
    return;
  }
  if (stuckUpdate == lastUpdate) {
    stuckCount += 1;
    if (stuckCount >= stuckThreshold) {
      // We are probably stuck, so kick the tires
      log += 'Kicking the tires\n';
      initializeAndStart();
      getViewValues();
    }
  }
  else {
    stuckUpdate = lastUpdate;
    stuckCount = 0;
  }
}

function setOverlay() {
  var screenOverlay = ge.createScreenOverlay('');
  screenOverlay.setIcon(ge.createIcon(''));
  screenOverlay.getIcon().setHref("http://bravo-369.appspot.com/viewer/extra-300.png");
  screenOverlay.getOverlayXY().setXUnits(ge.UNITS_FRACTION);
  screenOverlay.getOverlayXY().setYUnits(ge.UNITS_FRACTION);
  screenOverlay.getOverlayXY().setX(0);
  screenOverlay.getOverlayXY().setY(1);
  screenOverlay.getScreenXY().setXUnits(ge.UNITS_FRACTION);
  screenOverlay.getScreenXY().setYUnits(ge.UNITS_FRACTION);
  screenOverlay.getScreenXY().setX(0);
  screenOverlay.getScreenXY().setY(1);
  screenOverlay.getSize().setXUnits(ge.UNITS_FRACTION);
  screenOverlay.getSize().setYUnits(ge.UNITS_FRACTION);
  screenOverlay.getSize().setX(1);
  screenOverlay.getSize().setY(1);
  ge.getFeatures().appendChild(screenOverlay);
}

function failureCB(errorCode) {
}

function getNextView() {
  if (!positionData)
    return;
  var position = getNextPosition();
  lastPosition = position;
  setView(position);
}

function latLonToHeading(position1, position2) {
  if (!position1 || !position2)
    return lastHeading;
  var lat1 = Number(position1.lat).toRad();
  var lat2 = Number(position2.lat).toRad();
  var lon1 = Number(position1.lon).toRad();
  var lon2 = Number(position2.lon).toRad();
  var dLon = lon2 - lon1;
  var y = Math.sin(dLon) * Math.cos(lat2);
  var x = Math.cos(lat1)*Math.sin(lat2) -
          Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
  var heading = Math.atan2(y, x).toDeg() + 360;
  heading %= 360;
  if (lastHeading) {
    var timeDelta = (position1.timestamp - lastHeading.timestamp) / 1000;
    var change = lastHeading.difference(heading);
    change = lastHeading.rateLimitChange(change, timeDelta);
    heading = lastHeading.value + change;
  }
  lastHeading = Heading(heading, position1.timestamp);
  return lastHeading.toMagneticHeading();
}

function setView(position) {
  if (!position) return;
  var lat = position.lat;
  var lon = position.lon;
  var alt = position.alt;
  var heading = latLonToHeading(position, positionData[index+1].Position); //position.heading;
  var pitch = 110.0 + position.pitch;
  var roll = position.roll;
  var camera = ge.getView().copyAsCamera(ge.ALTITUDE_ABSOLUTE);
  camera.setLatitude(lat);
  camera.setLongitude(lon);
  camera.setAltitude(alt);
  camera.setHeading(heading);
  camera.setTilt(pitch);
  camera.setRoll(roll);
  ge.getView().setAbstractView(camera);
}

function getNextPosition() {
  if (hold) { return null; }
  var simNow = new Date().getTime() - timeDiff;
  while (index < positionData.length - 1 && simNow >= positionData[index+1].Position.timestamp) {
    index += 1;
  }
  if (index >= positionData.length - 1) {
    getViewValues();
    index = 0;
    return null;
  }
  var position1 = positionData[index].Position;
  var position2 = positionData[index+1].Position;
  var baseTime = simNow - position1.timestamp;
  if (baseTime == 0) {
    return position1;
  }
  else {
    var percent = baseTime / (position2.timestamp - position1.timestamp);
    var linearPosition = {};
    linearPosition.lat = position1.lat + (percent * (position2.lat - position1.lat));
    linearPosition.lon = position1.lon + (percent * (position2.lon - position1.lon));
    linearPosition.alt = position1.alt + (percent * (position2.alt - position1.alt));
    linearPosition.pitch = position1.pitch + (percent * (position2.pitch - position1.pitch));
    linearPosition.roll = position1.roll + (percent * (position2.roll - position1.roll));
    linearPosition.heading = position1.heading + (percent * (position2.heading - position1.heading));
    linearPosition.timestamp = position1.timestamp + (percent * (position2.timestamp - position1.timestamp));
    lastUpdate = position1.timestamp;
    return linearPosition;
  }
}

function getViewValues() {
  hold = true;
  var request = makeHttpObject();
  var path = "/api.html?method=positions&session=" + getQueryValue("session") + '&start_time=' + lastUpdate;
  request.open("GET", path, true);
  request.setRequestHeader("Authorization", "AZZT12212056");
  request.send();
  request.onreadystatechange = function() {
    if (request.readyState==4) {
      positionData_ = JSON.parse(request.responseText);
      positionData = [];
      var i = 0;
      for (i=0; i<positionData_.length; i++) {
        positionData[i] = JSON.parse(positionData_[i]);
      }
      if (timeDiff == 0) {
        timeDiff = startTime - positionData[0].Position.timestamp;
      }
      index = 0;
      hold = false;
      log += 'Updated. New data from ' + positionData[0].Position.timestamp + ' to ' + positionData[positionData.length - 1].Position.timestamp + '\n';
      if (positionData.length) getNextView();
    }
  }
}

function getQueryValue( name )
{
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
    return "";
  else
    return results[1];
}

function makeHttpObject() {
  try {return new XMLHttpRequest();}
  catch (error) {}
  try {return new ActiveXObject("Msxml2.XMLHTTP");}
  catch (error) {}
  try {return new ActiveXObject("Microsoft.XMLHTTP");}
  catch (error) {}

  throw new Error("Could not create HTTP request object.");
}